/*
 * Copyright (c) 2014 pkzd/1.0, 颜文泽(robin)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     1 Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     2 Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include	<fs.h>
#include	<elf.h>
#include	<asm.h>
#include	<sig.h>
#include	<tty.h>
#include	<map.h>
#include	<file.h>
#include	<errno.h>
#include	<param.h>
#include	<string.h>

pid_t
fork(void)
{
	struct proc *p;

	if(!(p = newproc()))
		return -1;
	memmove(p->p_stack, (uint *)kstk, PAGESIZ);
	asm volatile("movl %%esp, %0" : "=a"(p->p_esp) : );
	if(!save((uint)p->p_stack + p->p_esp - kstk))
		return 0;
	p->p_esp -= NCONTEXT;
	p->p_stat = SREADY;
	sti();
	return p->p_pid;
}

int
execve(char *filename, char **argv, char **envp)
{
	uint i, j, k;
	char buf[BLKSIZ];
	struct inode *ip;
	struct elfhd *elf;
	struct elfphd *phd;
	char cmd[VGA_COL_SIZ], *argp[NARGV];

	if(!(ip = namei(filename, 0, 0))){
		up->p_error = ENOENT;
		return -1;	
	}
	if(access0(ip, S_IXUSR) || !S_ISREG(ip->i_mode)){
		iput(ip);
		up->p_error = ENOEXEC;
		return -1;
	}
	if(log_read(0, buf, ip, 1) != 1){
		iput(ip);
		up->p_error = EIO;
		return -1;
	}
	elf = (struct elfhd *)buf;
	if(elf->e_magic != ELF_MAGIC || elf->e_type != ET_EXEC){
		iput(ip);
		up->p_error = ENOEXEC;
		return -1;
	}	
	j = 0;
	for(i = 0; argv && argv[i]; i++){
		if(i == (NARGV - 2))
			break;
		argp[i] = &cmd[j];
		memmove(cmd + j, argv[i], k = (strlen(argv[i]) + 1));	
		j += k;
	}	
	argp[i] = NULL;
	for(i = 3; i < NOFILE; i++)
		close(i);
	freeproc(up);
	up->p_send = STOP;
	phd = (struct elfphd *)((uchar *)elf + elf->e_phoff);
	for(i = 0, j = elf->e_phnum; i < j; i++, phd++){ 
		if(phd->e_type != PT_LOAD || !phd->e_memsiz)
			continue;
		if(i == 0)	
			up->p_start = up->p_end = phd->e_vaddr; 
		up->p_brk = up->p_end = up->p_end + RND_PAGE_SIZ(phd->e_memsiz);
		load_exec(phd->e_offset, phd->e_vaddr, phd->e_filesiz, ip);
	}
	iput(ip);
	save_exec(load_argv(argp), elf->e_entry); 
}

int
exit(int status)
{
	uint i;
	struct proc *p, *rp;	
	
	p = up;
	for(i = 0; i < NSIG; i++) 
		p->p_signal[i] = SIG_IGN; 
	if(p->p_pgrp && p->p_pid == p->p_pgrp && p->p_ttyp && 
		((struct tty *)p->p_ttyp)->t_pgrp == p->p_pgrp){
		((struct tty *)p->p_ttyp)->t_pgrp = 0; 
		signal(p->p_pgrp, SIGHUP);
	}
	p->p_pgrp = 0;
	for(i = 0; i < NOFILE; i++) 
		close(i);
	if(p->p_cdir) 
		iput(p->p_cdir);
	if(p->p_rdir) 
		iput(p->p_rdir);
	freeproc(p);
	p->p_stat = SZOMB;
	p->p_exit = status;
	for(rp = &proc[0]; rp < &proc[NPROC]; rp++){
		if(rp->p_ppid == p->p_pid){
			rp->p_ppid = 1;
			if(rp->p_stat == SZOMB)
				psignal(&proc[1], SIGCHLD);
		}
		if(p->p_ppid == rp->p_pid)
			psignal(rp, SIGCHLD);
		if(p->p_pid == rp->p_pgrp)	
			rp->p_pgrp = 0;
	}
	swtch();
}

pid_t
wait(int *status)
{
	uint s, f;
	struct proc *p;

loop:
	s = 0;
	for(p = &proc[0]; p < &proc[NPROC]; p++){
		if(p->p_ppid == up->p_pid){
			s++;	
			if(p->p_stat == SZOMB){
				f = p->p_pid;
				if(status)
					*status	= p->p_exit;
				p->p_sig = 0;
				p->p_pid = 0;
				p->p_exit = 0;
				p->p_ppid = 0;
				p->p_pgrp = 0;
				p->p_flag = 0;	
				p->p_wchan = NULL;
				p->p_stat = SFREE;
				mfree(kernmap, 1, (uint)p->p_pgdir);
        			mfree(kernmap, 1, (uint)p->p_stack);
				up->p_time += p->p_time;
				p->p_time = NULL;
				return f;
			}
		}
	}
	if(s){
		sleep(up, PWAIT);
		goto loop;
	}
	up->p_error = ECHILD;
	return -1;
}

pid_t 
getpid(void)
{
	return up->p_pid;
}

pid_t 
getppid(void)
{
	return up->p_ppid;
}

uid_t
getuid(void)
{
	return up->p_ruid;
}

uid_t 
geteuid(void)
{
	return up->p_uid;
}

gid_t
getgid(void)
{
	return up->p_rgid;
}

gid_t
getegid(void)
{
	return up->p_gid;
}

int
setpgid(void)
{
	up->p_pgrp = up->p_pid;
}

int
brk(uint addr)
{
	addr &= PAGESIZ_MASK;
	if(addr < up->p_end || addr > up->p_send)
		return -1;
	up->p_brk = addr; 
}
